package com.anita.member.domain.model.member;

import com.anita.member.common.enums.Resp;
import com.anita.member.common.exception.BusinessException;
import com.anita.member.domain.event.DomainEvent;
import com.anita.member.domain.event.member.MemberCreatedEvent;
import com.anita.member.domain.event.member.MemberPasswordChangedEvent;
import com.anita.member.domain.model.Gender;
import lombok.AccessLevel;
import lombok.Getter;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 会员聚合根
 * @author qiu
 * @date 2025/10/16
 * @description
 */
@Getter
public class Member {

    public Member(MemberId id, Username userName, Nickname nickName, Password password, Phone phone, Email email, Avatar avatar){
        this(id, userName, nickName, password, phone, email, avatar, "");
    }

    public Member(MemberId id, Username userName, Nickname nickName, Password password, Phone phone, Email email, Avatar avatar, String createdBy){
        if(id == null){
            throw new BusinessException(Resp.INVALID_PARAM);
        }
        if(userName == null){
            throw new BusinessException(Resp.INVALID_PARAM);
        }
        if(password == null){
            throw new BusinessException(Resp.INVALID_PARAM);
        }
        if(email == null){
            throw new BusinessException(Resp.INVALID_PARAM);
        }
        if(phone == null){
            throw new BusinessException(Resp.INVALID_PARAM);
        }

        this.id = id;
        this.userName = userName;
        this.nickName = nickName;
        this.password = password;
        this.email = email;
        this.phone = phone;
        this.avatar = new Avatar("");
        this.gender = Gender.MALE;
        this.status = Status.NORMAL;
        this.emailVerified = EmailVerified.UNVERIFIED;
        this.phoneVerified = PhoneVerified.UNVERIFIED;
        this.lastLoginAt = LocalDateTime.now();
        this.loginCount = 0;
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();

        registerEvent(new MemberCreatedEvent(this));
    }


    /**
     * 用于从持久化层恢复Member对象的静态方法
     * 不进行业务验证，因为数据已经存在
     */
    public static Member restore(MemberId id, Username userName, Nickname nickName, Password password, Email email,
                             Phone phone, Avatar avatar, Gender gender, LocalDateTime birthday, Location location,
                             String bio, String referralCode, Status status, EmailVerified emailVerified,
                             PhoneVerified phoneVerified, LocalDateTime lastLoginAt, String lastLoginIp, int loginCount,
                             LocalDateTime createdAt, LocalDateTime updatedAt, LocalDateTime deletedAt, String createdBy,
                             String updatedBy){
        Member member = new Member();
        member.id = id;
        member.userName = userName;
        member.nickName = nickName;
        member.password = password;
        member.email = email;
        member.phone = phone;
        member.avatar = avatar;
        member.gender = gender;
        member.birthday = birthday;
        member.location = location;
        member.bio = bio;
        member.referralCode = referralCode;
        member.status = status;
        member.emailVerified = emailVerified;
        member.phoneVerified = phoneVerified;
        member.lastLoginAt = lastLoginAt;
        member.lastLoginIp = lastLoginIp;
        member.loginCount = loginCount;
        member.createdAt = createdAt;
        member.updatedAt = updatedAt;
        member.deletedAt = deletedAt;
        member.createdBy = createdBy;
        member.updatedBy = updatedBy;
        return member;
    }

    /**
     * 私有构造函数，用于restore方法
     */
    private Member() {
        // events字段已经在声明时初始化，不需要再次赋值
    }

    private MemberId id;
    private Username userName;
    private Nickname nickName;
    private Password password;
    private Email email;
    private Phone phone;
    private Avatar avatar;
    private Gender gender;
    private LocalDateTime birthday;
    private Location location;
    private String bio;
    private String referralCode;
    private Status status;
    private EmailVerified emailVerified;
    private PhoneVerified phoneVerified;
    private LocalDateTime lastLoginAt;
    private String lastLoginIp;
    private int loginCount;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    private LocalDateTime deletedAt;
    private String createdBy;
    private String updatedBy;

    @Getter(value = AccessLevel.NONE)
    private final List<DomainEvent> events = new ArrayList<>();

    private void registerEvent(DomainEvent event) {
        events.add(event);
    }

    public void clearEvents() {
        events.clear();
    }

    public String getEncryptedPassword() {
        return password.getValue();
    }

    public void changePassword(String oldPassword, String newPassword, PasswordEncoder passwordEncoder) {
        if(!password.verify(oldPassword, passwordEncoder)){
            throw new BusinessException(Resp.USER_PASSWORD_ERROR);
        }
        this.password = Password.fromRaw(newPassword, passwordEncoder);
        this.updatedAt = LocalDateTime.now();
        registerEvent(new MemberPasswordChangedEvent(this.id));
    }

    public void resetPassword(String newPassword, PasswordEncoder passwordEncoder){
        this.password = Password.fromRaw(newPassword, passwordEncoder);
        this.updatedAt = LocalDateTime.now();
    }

    public boolean matchesPassword(String password, PasswordEncoder passwordEncoder){
        return this.password.verify(password, passwordEncoder);
    }

    public void updateAvatar(Avatar newAvatar){
        this.avatar = newAvatar;
        this.updatedAt = LocalDateTime.now();
    }

    public void updatePhone(Phone newPhone){
        this.phone = newPhone;
        this.updatedAt = LocalDateTime.now();
    }

    public void updateEmail(Email newEmail){
        this.email = newEmail;
        this.updatedAt = LocalDateTime.now();
    }

    public void recordLogin(String ip){
        this.lastLoginAt = LocalDateTime.now();
        this.lastLoginIp = ip;
        this.loginCount++;
        this.updatedAt = LocalDateTime.now();
    }

    public void setStatus(Status status){
        this.status = status;
        this.updatedAt = LocalDateTime.now();
    }

    public void changeBio(String bio){
        this.bio = bio;
        this.updatedAt = LocalDateTime.now();
    }
}
